package redis

import (
	"errors"
	"fmt"
	red "github.com/go-redis/redis/v8"
	"log"
)

func NewRedisIns(c *RdsConfig) (*RedisIns, error) {
	rdsInstance := &RedisIns{}
	e := rdsInstance.initConf(c).connect()
	if e != nil {
		return nil, e
	}
	if c.InitScript {
		rdsInstance.initInnerScript()
	}
	rdsMap[c.Name] = rdsInstance
	return rdsInstance, nil
}

func InitSingletonRds(c *RdsConfig) (*RedisIns, error) {
	if rdsSingletonIns != nil {
		return rdsSingletonIns, nil
	}
	if c.Name != "default" {
		c.Name = "default"
	}
	rds, e := NewRedisIns(c)
	if e != nil {
		return nil, e
	}
	rdsSingletonIns = rds
	return rdsSingletonIns, nil
}

func GetRedisClient(name ...string) *red.Client {
	if len(name) == 0 {
		return rdsSingletonIns.ins
	}
	if _, e := rdsMap[name[0]]; e {
		return rdsMap[name[0]].ins
	} else {
		return nil
	}
}

func GetRedisIns(name ...string) *RedisIns {
	if len(name) == 0 {
		return rdsSingletonIns
	}
	if _, e := rdsMap[name[0]]; e {
		return rdsMap[name[0]]
	} else {
		return nil
	}
}

func GetInnerScript(name ...string) *InnerScripts {
	if len(name) == 0 {
		return rdsSingletonIns.scripts
	}
	if _, e := rdsMap[name[0]]; e {
		return rdsMap[name[0]].scripts
	} else {
		return nil
	}
}

func Close(rdsIdx ...string) {
	if len(rdsIdx) == 0 {
		if rdsSingletonIns != nil {
			_ = rdsSingletonIns.ins.Close()
			rdsSingletonIns = nil
			delete(rdsMap, "default")
		}
	} else {
		for _, idx := range rdsIdx {
			if r, e := rdsMap[idx]; e {
				_ = r.ins.Close()
				delete(rdsMap, idx)
			}
		}
	}

}

func Exec(cmd string, params ...interface{}) red.Cmder {
	if rdsSingletonIns != nil {
		return rdsSingletonIns.exec(cmd, params...)
	} else {
		res := &red.Cmd{}
		res.SetErr(errors.New("redis instance was not init"))
		return res
	}
}

func Exec2Parse(cmd string, params ...interface{}) (interface{}, error) {
	if rdsSingletonIns != nil {
		return rdsSingletonIns.exec2parse(cmd, params...)
	}
	return nil, errors.New("redis instance was not init")
}

func DoExec(rdsIdx, cmd string, params ...interface{}) red.Cmder {
	if r, e := rdsMap[rdsIdx]; e {
		return r.exec(cmd, params...)
	} else {
		res := &red.Cmd{}
		res.SetErr(fmt.Errorf("redis %v was not exists", rdsIdx))
		return res
	}
}

// 订阅会导致对应连接阻塞，订阅请单独建立连接
func DoExec2Parse(rdsIdx, cmd string, params ...interface{}) (interface{}, error) {
	if rdsIdx == "" {
		rdsIdx = "default"
	}
	if r, e := rdsMap[rdsIdx]; e {
		return r.exec2parse(cmd, params...)
	} else {
		return nil, fmt.Errorf("redis %v was not exists", rdsIdx)
	}
}

// 订阅会导致对应连接阻塞，订阅请单独建立连接
func DoSubscribe(rdsIdx string, fCbk func(m *SubMsg), channel ...string) error {
	if rdsIdx == "" {
		rdsIdx = "default"
	}
	r, e := rdsMap[rdsIdx]
	if !e {
		return fmt.Errorf("redis %v was not exists", rdsIdx)
	}
	psc := r.subscribe(channel...)

	go func() {
		defer func() {
			if p := recover(); p != nil {
				log.Printf("Error in subscribe receive: %v\n", p)
			}
			_ = psc.Close()
		}()
		for {
			msg, err := psc.ReceiveMessage(r.ctx)
			if err != nil {
				panic(err)
			}
			fCbk(msg)
		}
	}()
	return nil
}

func DoPSubscribe(rdsIdx string, fCbk func(m *SubMsg), patten ...string) error {
	if rdsIdx == "" {
		rdsIdx = "default"
	}
	r, e := rdsMap[rdsIdx]
	if !e {
		return fmt.Errorf("redis %v was not exists", rdsIdx)
	}
	psc := r.psubscribe(patten...)

	go func() {
		defer func() {
			if p := recover(); p != nil {
				log.Printf("Error in psubscribe receive: %v\n", p)
			}
			_ = psc.Close()
		}()
		for {
			msg, err := psc.ReceiveMessage(r.ctx)
			if err != nil {
				panic(err)
			}
			fCbk(msg)
		}
	}()
	return nil
}

func NewScript(rdsIdx string, luaScript string) (*Script, error) {
	if rdsIdx == "" {
		rdsIdx = "default"
	}

	if r, e := rdsMap[rdsIdx]; !e {
		return nil, fmt.Errorf("redis %v was not exists", rdsIdx)
	} else {
		rdScript := red.NewScript(luaScript)
		_hash := rdScript.Hash()
		if v, e := r.scriptMap[_hash]; e {
			return v, nil
		}
		if err := rdScript.Load(r.ctx, r.ins).Err(); err != nil {
			return nil, err
		}
		sc := &Script{
			rdsIns: r,
			script: rdScript,
		}
		r.scriptMap[_hash] = sc
		return sc, nil
	}
}

// GEORADIUS
func DoGeoRadius(rdsIdx, key string, longitude, latitude float64, query *red.GeoRadiusQuery) ([]red.GeoLocation, error) {
	if rdsIdx == "" {
		rdsIdx = "default"
	}
	r, e := rdsMap[rdsIdx]
	if !e {
		return nil, fmt.Errorf("redis %v was not exists", rdsIdx)
	}
	return r.ins.GeoRadius(r.ctx, key, longitude, latitude, query).Result()
}

// GEORADIUSBYMEMBER
func DoGeoRadiusByMember(rdsIdx, key, member string, query *red.GeoRadiusQuery) ([]red.GeoLocation, error) {
	if rdsIdx == "" {
		rdsIdx = "default"
	}
	r, e := rdsMap[rdsIdx]
	if !e {
		return nil, fmt.Errorf("redis %v was not exists", rdsIdx)
	}
	return r.ins.GeoRadiusByMember(r.ctx, key, member, query).Result()
}
